package org.flexthinker.module.impl
{
    import flash.events.EventDispatcher;
    import flash.system.ApplicationDomain;
    import flash.system.SecurityDomain;
    import flash.utils.ByteArray;
    import flash.utils.describeType;
    
    import mx.core.IFlexModuleFactory;
    import mx.core.UIComponent;
    import mx.events.ModuleEvent;
    import mx.logging.ILogger;
    import mx.logging.Log;
    import mx.modules.IModuleInfo;
    import mx.modules.ModuleManager;
    
    import org.flexthinker.module.Descriptor;
    import org.flexthinker.module.DescriptorLoader;

    /**
     * Main Implementation for <code>DescriptorLoader</code>
     * Exposes same methods as IModuleInfo
     */
    public class DescriptorLoaderImpl extends EventDispatcher implements DescriptorLoader
    {
        private var log:ILogger = Log.getLogger("org.flexthinker.module.impl.DescriptorLoaderImpl");

        protected var moduleInfo:IModuleInfo;

        private var _descriptor:Descriptor;
		
		protected var moduleFactory:IFlexModuleFactory;

        public function DescriptorLoaderImpl(descriptor:Descriptor)
        {
            this._descriptor = descriptor;
            this.moduleInfo = ModuleManager.getModule(descriptor.url);

        }

        public function get descriptor():Descriptor
        {
            return _descriptor;
        }

        public function get data():Object
        {
            return moduleInfo.data;
        }

        public function set data(value:Object):void
        {
            moduleInfo.data = value;
        }

        public function get error():Boolean
        {
            return moduleInfo.error;
        }

        public function get factory():IFlexModuleFactory
        {
            return moduleInfo.factory;
        }

        public function get loaded():Boolean
        {
            return moduleInfo.loaded;
        }

        public function get ready():Boolean
        {
            return moduleInfo.ready;
        }

        public function get setup():Boolean
        {
            return moduleInfo.setup;
        }

        public function get url():String
        {
            return moduleInfo.url;
        }

        public function load(applicationDomain:ApplicationDomain = null, securityDomain:SecurityDomain = null,
                bytes:ByteArray = null, moduleFactory:IFlexModuleFactory = null):void
        {
            if (applicationDomain || securityDomain || bytes )
            {
                throw new Error("To control load parameters use instead the ModuleDescriptor.");
            }

            moduleInfo.addEventListener(ModuleEvent.PROGRESS, decorateModuleEvent);
            moduleInfo.addEventListener(ModuleEvent.SETUP, decorateModuleEvent);
            moduleInfo.addEventListener(ModuleEvent.READY, decorateModuleEvent);
            moduleInfo.addEventListener(ModuleEvent.ERROR, decorateModuleEvent);
//            moduleInfo.addEventListener(ModuleEvent.UNLOAD, decorateModuleEvent);

            moduleInfo.load(descriptor.applicationDomain, descriptor.securityDomain, null, moduleFactory);
        }

        public function release():void
        {
            moduleInfo.release();
        }

        public function unload():void
        {
//            moduleInfo.addEventListener(ModuleEvent.UNLOAD, moduleInfo_unloadHandler);
			
			// forces a garbage collection when this module is unloaded
			// LoaderInfo(_moduleInstance.loaderInfo).loader.unloadAndStop(true);
			
            moduleInfo.unload();

            moduleInfo.removeEventListener(ModuleEvent.PROGRESS, decorateModuleEvent);
            moduleInfo.removeEventListener(ModuleEvent.SETUP, decorateModuleEvent);
            moduleInfo.removeEventListener(ModuleEvent.READY, decorateModuleEvent);
            moduleInfo.removeEventListener(ModuleEvent.ERROR, decorateModuleEvent);
//            moduleInfo.removeEventListener(ModuleEvent.UNLOAD, decorateModuleEvent);

            moduleUnloaded();
        }

        public function publish(factory:IFlexModuleFactory):void
        {
            moduleInfo.publish(factory);
        }

        /**
         * @private
         * Decorates the ModuleEvent in order to send the moduleInfo as <code>this</code> instead of the internal moduleInfo
         */
        private function decorateModuleEvent(event:ModuleEvent):void
        {
            var newEvent:ModuleEvent = new ModuleEvent(event.type, event.bubbles, event.cancelable, event.bytesLoaded,
                    event.bytesTotal, event.errorText, this);
            
            CONFIG::DEBUG
            {
                if (event.type == ModuleEvent.READY)
                {
                    log.debug("MODULE IS READY -> [[[ {0} - {1} ]]]", this, this.url);
                }
            }
			
			redispatchLoaderEvent(newEvent);
        }

        protected function redispatchLoaderEvent(event:ModuleEvent):void
        {
            dispatchEvent(event);
        }

        /**
         * Hook in case you have to do something special,
         * after a module has been unloaded.
         */
        protected function moduleUnloaded():void
        {

        }

        /** -------------------
         *    DescriptorLoader
         ------------------- */

        public function init(targetObject:UIComponent):void
        {
            if (!ready)
            {
                throw new Error("init() method can't be executed before the module is ready");
            }
        }

        /*
         private function moduleInfo_unloadHandler(event:ModuleEvent):void
         {
         var newEvent:ModuleEvent = new ModuleEvent(event.type, event.bubbles, event.cancelable, event.bytesLoaded,
         event.bytesTotal, event.errorText, this);
         redispatchLoaderEvent(newEvent);

         moduleInfo.removeEventListener(ModuleEvent.UNLOAD, moduleInfo_unloadHandler);

         moduleUnloaded();
         }
         */
    }
}